package com.timothy.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;

import com.timothy.remote.core.RemoteServiceInterface;

@Configuration
@EnableConfigurationProperties({ ServerShiroProperties.class })
public class ServerShiroConfig {

	/**
	 * redis管理者，服务于session缓存和授权缓存
	 * @param redisProperties
	 * @return
	 */
	@Bean
	public RedisManager redisManager(RedisProperties redisProperties) {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(redisProperties.getHost());
		redisManager.setPort(redisProperties.getPort());
		redisManager.setDatabase(redisProperties.getDatabase());
		redisManager.setPassword(redisProperties.getPassword());
		return redisManager;
	}

	/**
	 * 基于redis的session dao
	 * @param redisManager
	 * @param shiroProperties
	 * @return
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO(RedisManager redisManager, ServerShiroProperties shiroProperties) {
		RedisSessionDAO redisSessionDao = new RedisSessionDAO();
		redisSessionDao.setRedisManager(redisManager);
		redisSessionDao.setExpire(shiroProperties.getRedisSessionExpire());
		redisSessionDao.setKeyPrefix(shiroProperties.getRedisSessionPrefix());
		return redisSessionDao;
	}

	/**
	 * session id的cookie
	 * @return
	 */
	@Bean
	public SimpleCookie sessionIdCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("sid");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(-1);
		simpleCookie.setDomain("");
		simpleCookie.setPath("");
		return simpleCookie;
	}

	/**
	 * session 管理
	 * @param redisSessionDAO
	 * @param sessionIdCookie
	 * @param shiroProperties
	 * @return
	 */
	@Bean(name = "sessionManager")
	public SessionManager sessionManager(RedisSessionDAO redisSessionDAO, SimpleCookie sessionIdCookie,
			ServerShiroProperties shiroProperties) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		// 设置session过期时间
		sessionManager.setGlobalSessionTimeout(shiroProperties.getRedisSessionExpire() * 1000);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionIdUrlRewritingEnabled(false);
		sessionManager.setSessionDAO(redisSessionDAO);
		// sessionManager.setCacheManager(cacheManager);
		sessionManager.setSessionIdCookie(sessionIdCookie);
		sessionManager.setSessionIdCookieEnabled(true);
		return sessionManager;
	}

	/**
	 * cache管理
	 * @param redisManager
	 * @param shiroProperties
	 * @return
	 */
	@Bean(name = "cacheManager")
	public CacheManager cacheManager(RedisManager redisManager, ServerShiroProperties shiroProperties) {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager);
		redisCacheManager.setKeyPrefix(shiroProperties.getRedisCachePrefix());
		// 过期时间，秒值
		redisCacheManager.setExpire(shiroProperties.getRedisCacheExpire());
		// redisCacheManager.setValueSerializer();
		return redisCacheManager;
	}

	/**
	 * 安全管理
	 * @param userRealm
	 * @param sessionManager
	 * @param cacheManager
	 * @return
	 */
	@Bean(name = "securityManager")
	public SecurityManager securityManager(ServerShiroRealm userRealm, SessionManager sessionManager,
			CacheManager cacheManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setSessionManager(sessionManager);
		securityManager.setCacheManager(cacheManager);
		securityManager.setRealm(userRealm);
		// securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

	/**
	 * shiro filter
	 * @param securityManager
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

		// 必须设置 SecurityManager
		factoryBean.setSecurityManager(securityManager);
		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		factoryBean.setLoginUrl("/login.html");
		factoryBean.setUnauthorizedUrl("/");

		Map<String, String> filterMap = new LinkedHashMap<>();
		filterMap.put("/public/**", "anon");
		filterMap.put("/login.html", "anon");
		filterMap.put("/sys/login", "anon");
		filterMap.put("/remoteService", "anon");
		filterMap.put("/403", "anon");
		// session回调
		// filterMap.put("/cle/token", "anon");
		filterMap.put("/**", "user");
		factoryBean.setFilterChainDefinitionMap(filterMap);

		return factoryBean;
	}

	/**
	 * 远程权限请求接口专用
	 * @param remoteService
	 * @return
	 */
	@Bean(name = "/remoteService")
	public HttpInvokerServiceExporter HttpInvokerServiceExporter(ServerRmoteService remoteService) {
		HttpInvokerServiceExporter httpInvokerServiceExporter = new HttpInvokerServiceExporter();
		httpInvokerServiceExporter.setService(remoteService);
		httpInvokerServiceExporter.setServiceInterface(RemoteServiceInterface.class);
		return httpInvokerServiceExporter;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
		proxyCreator.setProxyTargetClass(true);
		return proxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}

}
